using System;
using System.Collections.Generic;
using System.Text;
using System.Xml;
using System.Xml.Serialization;
using System.IO;
using System.Runtime.InteropServices;
using System.Reflection;

namespace AnguaRPGEngine.Library.Systems
{
    /// <summary>
    /// This class utilizes C#'s Generics typing.  The ConfigType is passed as whatever object, and through reflection, is used as the actual type.
    /// </summary>
    /// <typeparam name="ConfigType"></typeparam>
    public class Serializer<ConfigType>
    {
        public static string ErrorMessage = "";
        
        #region DeserializeFromFile
        public static ConfigType DeserializeFromFile(string XMLFileName)
        {
            try
            {
                return (ConfigType)DeserializeObject(XMLFileName);
            }
            catch (Exception err)
            {
                ErrorMessage = "Failed to load XML file \"" + XMLFileName + "\"\r\n" + err.Message;
            }
            return default(ConfigType);
        }
        #endregion

        #region SerializeToFile
        public static bool SerializeToFile(ConfigType objToSave, string XMLFileName)
        {
            bool Success = true;
            try
            {
                if (!Directory.Exists(XMLFileName.Substring(0, XMLFileName.LastIndexOf("\\"))))
                {
                    Directory.CreateDirectory(XMLFileName.Substring(0, XMLFileName.LastIndexOf("\\")));
                }
                Success = SerializeObject(objToSave, XMLFileName);
            }
            catch (Exception err)
            {
                ErrorMessage = "Failed to save XML file \"" + XMLFileName + "\"\r\n" + err.Message;
                Success = false;
            }
            return Success;
        }
        #endregion

        #region DeserializeFromString
        public static ConfigType DeserializeFromString(string XMLText)
        {
            try
            {
                return (ConfigType)DeserializeObject(new StringBuilder(XMLText));
            }
            catch (Exception err)
            {
                ErrorMessage = "Failed to read XML from memory \"" + XMLText + "\"\r\n" + err.Message;
            }
            return default(ConfigType);
        }
        #endregion

        #region SerializeToString
        public static bool SerializeToString(ConfigType objToSave, out string XMLText)
        {
            bool Success = true;
            XMLText = "";
            try
            {
                StringBuilder SerializedObject = new StringBuilder();
                Success = SerializeObject(objToSave, out SerializedObject);
                XMLText = SerializedObject.ToString();
            }
            catch (Exception err)
            {
                ErrorMessage = "Failed to save XML to memory \"" + XMLText + "\"\r\n" + err.Message;
                Success = false;
            }
            return Success;
        }
        #endregion

        #region SerializeObject
        /// <summary>
        /// This overload uses a file to serialize to
        /// </summary>
        /// <param name="Object"></param>
        /// <param name="SchemaFileName"></param>
        /// <returns></returns>
        private static bool SerializeObject(ConfigType Object, string SchemaFileName)
        {
            bool success = false;

            XmlSerializer s = new XmlSerializer(typeof(ConfigType));//ObjectType);
            TextWriter w = new StreamWriter(SchemaFileName);
            s.Serialize(w, Object);
            w.Close();
            success = true;

            return success;
        }

        /// <summary>
        /// This overload will serialize the object to a memory stream and return it as a StringBuilder object
        /// </summary>
        /// <param name="Object"></param>
        /// <param name="SerializeObject"></param>
        /// <returns></returns>
        private static bool SerializeObject(ConfigType Object, out StringBuilder SerializeObject)
        {
            bool success = false;

            XmlSerializer s = new XmlSerializer(typeof(ConfigType));//ObjectType);
            MemoryStream w = new MemoryStream();
            s.Serialize(w, Object);
            w.Flush();
            w.Close();
            byte[] Buffer = w.ToArray();
            SerializeObject = new StringBuilder(Encoding.ASCII.GetString(Buffer));
            success = true;

            return success;
        }
        #endregion

        #region DeserializeObject
        /// <summary>
        /// This overload uses a file to obtain the object from
        /// </summary>
        /// <param name="SchemaFileName"></param>
        /// <returns></returns>
        private static ConfigType DeserializeObject(string SchemaFileName)
        {
            ConfigType o = default(ConfigType);

            XmlSerializer s = new XmlSerializer(typeof(ConfigType));
            TextReader r = new StreamReader(SchemaFileName);
            o = (ConfigType)s.Deserialize(r);
            r.Close();

            return o;
        }

        /// <summary>
        /// This overloaded method allows you to deserialize from a stringbuilder representation of the object
        /// </summary>
        /// <param name="SerializedObject"></param>
        /// <returns></returns>
        private static ConfigType DeserializeObject(StringBuilder SerializedObject)
        {
            ConfigType o = default(ConfigType);

            XmlSerializer s = new XmlSerializer(typeof(ConfigType));
            byte[] Buffer = (new UnicodeEncoding()).GetBytes(SerializedObject.ToString());
            MemoryStream r = new MemoryStream(Buffer);
            o = (ConfigType)s.Deserialize(r);
            r.Close();

            return o;
        }
        #endregion
    }
}
